#ifndef __NetworkEventMonitor_h__
#define __NetworkEventMonitor_h__

#include <interfaces/SystemSocket.h>
#include <interfaces/TimerEventBase.h>
#include <interfaces/SocketEventMonitor.h>

#include <sys/select.h>

#include <set>

#include <time.h>

#include <stdio.h>

using namespace Greatewall::Interface;

namespace Greatewall
{
    namespace Network
    {

        class SystemEventMonitor : public SocketEventMonitor
        {
        public:
            class TimerEvent : public TimerEventBase
            {
                friend class TimerEventManager;
            public:
                enum State
                {
                    STARTED,
                    STOPPED
                };

                TimerEvent(timespec& interval, bool periodic, TimerEventManagerBase& timer_event_manager)
                    : _state(STOPPED)
                    , _interval(interval)
                    , _periodic(periodic)
                    , _event_id(timer_event_manager.getTimerEventId())
                    , _timer_event_manager(timer_event_manager)
                {
                    _base.tv_sec  = 0;
                    _base.tv_nsec = 0;
                }

                virtual
                ~TimerEvent()
                {
                    stop();
                }

                virtual bool
                start()
                {
                    if (_state == STARTED)
                    {
                        // TODO: Error handling
                        return false;
                    }

                    // Register the event
                    if (!_timer_event_manager.registerTimerEvent(this))
                    {
                        // TODO: Error handling
                        return false;
                    }

                    _state = STARTED;

                    return true;
                }

                virtual bool
                stop()
                {
                    if (_state == STOPPED)
                    {
                        // TODO: Error handling

                        return false;
                    }

                    // Unregister the event
                    if (!_timer_event_manager.unregisterTimerEvent(this))
                    {
                        // TODO: Error handling
                        return false;
                    }


                    _state = STOPPED;
                    return true;
                }

                virtual bool
                periodic()
                { return _periodic; }

                virtual long
                id()
                { return _event_id; }

                long
                sec() const
                { return _base.tv_sec; }

                long
                nsec() const
                { return _base.tv_nsec; }

                virtual long
                cmpKey1() const
                { return sec(); }

                virtual long
                cmpKey2() const
                { return nsec(); }

                virtual long
                cmpKey3() const
                { return _event_id; }

                void
                setBaseTimeStamp(timespec& base)
                {
                    _base.tv_nsec = base.tv_nsec + _interval.tv_nsec;
                    if (_base.tv_nsec % 1000000000 == _base.tv_nsec)
                        _base.tv_sec = _interval.tv_sec + base.tv_sec;
                    else
                    {
                        _base.tv_nsec = _base.tv_nsec % 1000000000;
                        _base.tv_sec = _interval.tv_sec + base.tv_sec + 1;
                    }
                }

            protected:
                State     _state;
                timespec  _base;
                timespec  _interval;
                bool      _periodic;
                long      _event_id;
                TimerEventManagerBase& _timer_event_manager;
            };

        protected:
            class TimerEventManager : public TimerEventManagerBase
            {
                friend class TimerEvent;
            public:
                TimerEventManager()
                    : _next_id(0)
                { updateBaseTimeStamp(); }

                virtual long
                getTimerEventId()
                { return _next_id++; }

                virtual void
                checkTimerEvents()
                {
                    if (_timer_events.empty())
                        return;

                    while(_timer_events.size())
                    {
                        TimerEvent* event = *_timer_events.begin();

                        //printf("event(%ld)[%d]: %ld - %ld\n", event->id(), _timer_events.size(), event->sec(), event->nsec());
                        if ( event->cmpKey1() > _base_time_stamp.tv_sec ||
                                (event->cmpKey1() == _base_time_stamp.tv_sec &&
                                 event->cmpKey2() >  _base_time_stamp.tv_nsec)
                           )
                            break;
                        // Do the stuff...
                        // ATTENTION: this must be kept as short as possible
                        // Should not be blocked
                        event->triggered();
                        event->stop();
                        if (event->periodic())
                            event->start();
                    };
                }

                const timespec*
                getClosestTimeoutPeriod()
                {
                    if (!_timer_events.size())
                        return NULL;
                    static timespec closest;
                    TimerEvent* event = *_timer_events.begin();
                    updateBaseTimeStamp();
                    long nsec_delta = event->nsec() - _base_time_stamp.tv_nsec;
                    closest.tv_sec  = nsec_delta >= 0
                                    ? event->sec() - _base_time_stamp.tv_sec
                                    : event->sec() - _base_time_stamp.tv_sec - 1;
                    closest.tv_nsec = nsec_delta >= 0 ? nsec_delta : 1000000000 + nsec_delta;
                    /*
                       printf("-------------\n");
                       printf("Base: %ld - %ld - %ld\n", _base_time_stamp.tv_sec, _base_time_stamp.tv_nsec, nsec_delta);
                       printf("event(%ld): %ld - %ld\n", event->id(), event->sec(), event->nsec());
                       printf("Closest: %ld - %ld\n", closest.tv_sec, closest.tv_nsec);
                       */

                    // The event has expired, so we need to timeout immediately
                    if (closest.tv_sec < 0)
                    {
                        closest.tv_sec = 0;
                        closest.tv_nsec = 0;
                    }

                    return &closest;
                }

                virtual char*
                getTimeStamp()
                {
                    updateBaseTimeStamp();
                    static char timestamp[128] = {0};
                    sprintf(timestamp, "%02ld:%02ld:%02ld.%03ld.%06ld",
                            _base_time_stamp.tv_sec / 3600,
                            _base_time_stamp.tv_sec % 3600 / 60,
                            _base_time_stamp.tv_sec % 60,
                            _base_time_stamp.tv_nsec / 1000000,
                            _base_time_stamp.tv_nsec % 1000000
                           );

                    return timestamp;
                }

                virtual long
                getNumberOfEvents()
                { return _timer_events.size(); }

            protected:
                void
                updateBaseTimeStamp()
                {
                    clock_gettime(CLOCK_MONOTONIC, &_base_time_stamp);
                }

                virtual bool
                registerTimerEvent(TimerEventBase* event_base, bool update_base_time_stamp = true)
                {
                    TimerEvent* event = dynamic_cast<TimerEvent*>(event_base);

                    if (!event)
                    {
                        // TODO: Error handling
                        return false;
                    }

                    if (update_base_time_stamp)
                        updateBaseTimeStamp();

                    event->setBaseTimeStamp(_base_time_stamp);

                    std::pair<TimerEvents::iterator, bool> res = _timer_events.insert(event);

                    if (!res.second)
                    {
                        // TODO: Error handling
                        printf("unable to insert event (%ld)", event->id());
                        return false;
                    }

                    return true;
                }

                bool
                unregisterTimerEvent(TimerEventBase* event_base)
                {
                    TimerEvent* event = dynamic_cast<TimerEvent*>(event_base);

                    if (!event)
                    {
                        // TODO: Error handling
                        return false;
                    }

                    TimerEvents::iterator find = _timer_events.find(event);

                    if (find == _timer_events.end())
                    {
                        // TODO: Error handling
                        return false;
                    }
                    _timer_events.erase(find);

                    return true;
                }

            private:
                long      _next_id;
                timespec  _base_time_stamp;

                class TimerEventCompare : public std::binary_function<TimerEventBase*, TimerEventBase*, bool>
                {
                    public:
                        bool
                        operator()(TimerEventBase* left, TimerEventBase* right) const
                        {
                            return  left->cmpKey1() < right->cmpKey1() ? true  :
                                    left->cmpKey1() > right->cmpKey1() ? false :
                                    left->cmpKey2() < right->cmpKey2() ? true  :
                                    left->cmpKey2() > right->cmpKey2() ? false :
                                    left->cmpKey3() < right->cmpKey3() ? true  : false;
                        }
                };

                typedef std::set<TimerEvent*, TimerEventCompare> TimerEvents;
                TimerEvents _timer_events;
            };

        public:

            SystemEventMonitor();

            virtual bool
            registerSocket(SystemSocket* sock, SocketEventType event_type);

            virtual bool
            unregisterSocket(SystemSocket* sock);

            virtual void
            run();

            TimerEventManager&
            getTimerEventManager()
            { return _timer_event_manager; }

        private:

            TimerEventManager _timer_event_manager;

            fd_set      _in_fd_set;
            fd_set      _out_fd_set;
            fd_set      _exp_fd_set;

            fd_set      _in;
            fd_set      _out;
            fd_set      _exp;

            class SystemSocketCompare : public std::binary_function<SystemSocket*, SystemSocket*, bool>
            {
            public:
                bool
                operator()(SystemSocket* left, SystemSocket* right)
                {
                    return left->hdl() < right->hdl();
                }
            };
            typedef std::set<SystemSocket*, SystemSocketCompare> SystemSocketSet;
            SystemSocketSet  _system_socks;

            bool        _stop_monitor;
        };
    }
}

#endif
